#pragma once
#include <iostream>
#include <string>
#include <filesystem>
#include <fstream>
#include <ctime>
#include <unistd.h>


#include "mutex.hpp"

using namespace mutex_module;

namespace LogModule
{
    static const char gsep = '\n';
    // 日志刷新的虚基类
    class SyncStrategy
    {
    public:
        SyncStrategy() = default;
        virtual void Sync(const std::string &message) = 0;
    };
    // 输出终端策略
    class ConsoleSyncStrategy : public SyncStrategy
    {
    public:
        void Sync(const std::string &message) override
        {
            LockGuard lg(_mutex);
            std::cout << message << gsep;
        }

    private:
        Mutex _mutex;
    };
    // 输出文件策略
    const char* file_path = "./log";
    const char* file_name = "mylog";
    class FileSyncStrategy : public SyncStrategy
    {
    public:
        FileSyncStrategy(std::string path = file_path, std::string file = file_name) //记得带上缺省值!!!
            : _path(path), _file(file)
        {
            LockGuard lg(_mutex);
            if (std::filesystem::exists(path))
            {
                return;
            }
            try
            {
                std::filesystem::create_directories(_path);
            }
            catch (std::filesystem::filesystem_error& error)
            {
                std::cout << error.what() << gsep;
            }
        }
        void Sync(const std::string &message) override
        {
            LockGuard lg(_mutex);
            std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;
            std::ofstream obj(filename, std::ios::app);
            obj << message;
        }

    private:
        std::string _path;
        std::string _file;
        Mutex _mutex;
    };

    // 日志等级
    enum class LogLevel
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    std::string LevelToStr(LogLevel lev) // 枚举类型的值为整形,需要转换
    {
        switch (lev)
        {
        case LogLevel::DEBUG:
            return "DEBUG";
        case LogLevel::INFO:
            return "INFO";
        case LogLevel::WARNING:
            return "WARNING";
        case LogLevel::ERROR:
            return "ERROR";
        case LogLevel::FATAL:
            return "FATAL";
        default:
            return "UNKONWN";
        }
    }

    // 日期时间
    std::string GetTime()
    {
        time_t now = time(nullptr);
        struct tm cur_time;

        localtime_r(&now, &cur_time);

        char timebuffer[256];
        snprintf(timebuffer, sizeof(timebuffer), "%04d-%02d-%02d %02d:%02d:%02d",
                 cur_time.tm_year + 1900,
                 cur_time.tm_mon + 1,
                 cur_time.tm_mday,
                 cur_time.tm_hour,
                 cur_time.tm_min,
                 cur_time.tm_sec);
        return timebuffer;
    }

    class Logger
    {
    public:
        Logger()
        {
            _ptr = std::make_unique<ConsoleSyncStrategy>(); // 默认向控制台输出
        }
        // 向终端输出的策略
        void EnableConsoleSync()
        {
            _ptr = std::make_unique<ConsoleSyncStrategy>();
        }
        // 向文件输出的策略
        void EnableFileSync() 
        { 
            _ptr = std::make_unique<FileSyncStrategy>(); 
        }

        class LogMessage
        {
        public:
            LogMessage(std::string timestamp, std::string level, std::string file, pid_t pid, int line, Logger &logger)
                : _timestamp(timestamp), _level(level), _file(file), _pid(pid), _line(line), _logger(logger)
            {
                std::stringstream ss;
                ss << "[" << _timestamp << "] "
                   << "[" << _level << "] "
                   << "[" << _pid << "] "
                   << "[" << _file << "] "
                   << "[" << _line << "] - ";
                _message = ss.str();
            }
            LogMessage &operator<<(const std::string input)
            {
                _message += input;
                _message += " ";
                return *this;
            }
            ~LogMessage()
            {
                _logger._ptr->Sync(_message);
            }

        private:
            std::string _timestamp;
            std::string _level;
            std::string _file;
            pid_t _pid;
            int _line;
            std::string _message;
            Logger &_logger;
        };
        LogMessage operator()(LogLevel level, std::string file, int line) // LogLevel& level ???
        {
            return LogMessage(GetTime(), LevelToStr(level), file, getpid(), line, *this);
        }

    private:
        std::unique_ptr<SyncStrategy> _ptr;
    };

    Logger logger;

    #define LOG(level) logger(level,__FILE__ , __LINE__)
    #define EnableConsoleSyncStrategy() logger.EnableConsoleSync()
    #define EnableFileSyncStrategy()  logger.EnableFileSync()

}

